#include "sgu.h"

SGU_NS_BEGIN

File::File(const char * name, int flags, mode_t mode, t_destructretry drt) :
	_drt(drt)
{
	_fd = ::open(name,flags,mode);
	if(_fd==-1) THROWERRNO();
}
	

File::~File()
{
	while(true)
	{	
		if(-1==::close(_fd))
		{
			if(errno==EINTR) continue;
			if(_drt)
			{
				if(_drt(errno,_fd,__PRETTY_FUNCTION__)) continue;
			}
			else logerr(errno,_fd,__PRETTY_FUNCTION__);
		}
		break;
	}
}


size_t File::write(const void * buffer, size_t size) const
{
	ssize_t written;
	while(true)
	{
		written = ::write(_fd,buffer,size);
		if(written==-1)
		{
			if(errno==EINTR) continue;
			THROWERRNO();
		}
		break;
	}
	return written;
}


size_t File::read(void * buffer, size_t size) const
{
	ssize_t readen;	
	while(true)
	{	
		readen = ::read(_fd,buffer,size);
		if(readen==-1)
		{
			if(errno==EINTR) continue;
			THROWERRNO();
		}
		break;
	}
	return readen;
}

void File::setOfs(off_t pos, int whence) const
{
	off_t x = lseek(_fd,pos,whence);
	if(x==(off_t)-1) THROWERRNO();
}

size_t File::getOfs() const
{
	off_t pos = lseek(_fd,0,SEEK_CUR);
	if(pos==(off_t)-1) THROWERRNO();
	return pos;
}

File::operator int() const
{
	return _fd;
}

size_t File::getSize() const
{
	struct stat a;
	ERRCALL(fstat(_fd,&a));
	return a.st_size;
}

void File::setSize(size_t n) const
{
	ERRCALL(ftruncate(_fd,n));
}



//********************** FileLock
FileLock::FileLock(File & f, bool write, bool wait, t_destructretry drt) : 
	_drt(drt),
	_file(f),
	_file_use(this,_file)
{
	//_file.addUser(this);
	struct flock k;
	memset(&k,0,sizeof(k));
	k.l_whence = SEEK_SET;
	k.l_start = 0;
	k.l_len = 0;
	k.l_type = write ? F_WRLCK : F_RDLCK;
	
	while(true)
	{
		if(-1==fcntl(_file, wait ? F_SETLKW : F_SETLK, &k))
		{
			if(errno==EINTR) continue;
			_file.delUser(this);
			THROWERRNO();
		}
		break;
	}
}

FileLock::~FileLock()
{
	struct flock k;
	memset(&k,0,sizeof(k));
	k.l_whence = SEEK_SET;
	k.l_start = 0;
	k.l_len = 0;
	k.l_type = F_UNLCK;
	
	while(true)
	{
		if(-1==fcntl(_file,F_SETLK,&k))
		{
			if(errno==EINTR) continue;
			if(_drt)
			{
				if(_drt(errno,_file,__PRETTY_FUNCTION__)) continue;
			}
			else logerr(errno,_file,__PRETTY_FUNCTION__);
		}
		break;
	}
	
	//_file.delUser(this);
}


//***** FileMappingConstSegment

FileMappingConstSegment::FileMappingConstSegment(const char * filename, t_destructretry drt) :
	_drt(drt),
	_file(filename,O_RDONLY,0,_drt),
	_lock(_file,false,false,_drt)
{
	_size = _file.getSize();
	_ptr = (char*)mmap(0,_size,PROT_READ,MAP_SHARED,_file,0);
	if(_ptr==MAP_FAILED) THROWERRNO();
}

FileMappingConstSegment::~FileMappingConstSegment()
{
	while(true)
	{	
		if(-1==munmap(_ptr,_size))
		{
			if(_drt)
			{
				if(_drt(errno,_file,__PRETTY_FUNCTION__)) continue;
			}
			else logerr(errno,_file,__PRETTY_FUNCTION__);
		}
		break;
	}
}

size_t FileMappingConstSegment::size() const
{
	return _size;
}

const char * FileMappingConstSegment::cptr() const
{
	return _ptr;
}


//***** FileMappingSegment

FileMappingSegment::FileMappingSegment(const char * filename, t_destructretry drt) :
	_drt(drt),
	_file(filename,O_RDWR,0,drt),
	_lock(_file,true,false,drt)
{
	_size = _file.getSize();
	_ptr = (char*)mmap(0,_size,PROT_READ|PROT_WRITE,MAP_SHARED,_file,0);
	if(_ptr==MAP_FAILED) THROWERRNO();
}

FileMappingSegment::~FileMappingSegment()
{
	while(true)
	{	
		if(-1==munmap(_ptr,_size))
		{
			if(_drt)
			{
				if(_drt(errno,_file,__PRETTY_FUNCTION__)) continue;
			}
			else logerr(errno,_file,__PRETTY_FUNCTION__);
		}
		break;
	}
}

size_t FileMappingSegment::size() const
{
	return _size;
}

const char * FileMappingSegment::cptr() const
{
	return _ptr;
}

char * FileMappingSegment::ptr() const
{
	return _ptr;
}



//**** FileMappingDynamicSegment
FileMappingDynamicSegment::FileMappingDynamicSegment(
	const char * filename,
	bool create,
	mode_t mode,
	size_t growby,
	t_destructretry drt
) :
	_drt(drt),
	_file(filename,O_RDWR|(create?O_CREAT:0),mode,drt),
	_lock(_file,true,false,drt)
{
	struct stat s;
	fstat((int)_file,&s);
	if(!S_ISREG(s.st_mode)) THROWSGUFUNCEXCEPTION("/nonregular");

	size_t gran = (sysconf(_SC_PAGESIZE) >? s.st_blksize);
	_growby = growby - growby % gran;
	_growby >?= gran;
	if(create) _truesize = 0, _file.setSize(growby);
	else _truesize = _file.getSize();
	map();
}

FileMappingDynamicSegment::~FileMappingDynamicSegment()
{	
	while(true)
	{
		if(-1==::munmap(_ptr,_size))
		{
			if(_drt)
			{
				if(_drt(errno,_file,__PRETTY_FUNCTION__)) continue;
			}
			else logerr(errno,_file,__PRETTY_FUNCTION__);
		}
		break;
	}
	
	while(true)
	{
		if(-1==::ftruncate((int)_file,_truesize))
		{
			if(_drt)
			{
				if(_drt(errno,_file,__PRETTY_FUNCTION__)) continue;
			}
			else logerr(errno,_file,__PRETTY_FUNCTION__);
		}
		break;
	}
}


void FileMappingDynamicSegment::map()
{
	_size = _file.getSize();
	_ptr = (char*)mmap(0,_size,PROT_READ|PROT_WRITE,MAP_SHARED,_file,0);
	if(_ptr==MAP_FAILED) THROWERRNO();
	ASSERT(_ptr);
}

void FileMappingDynamicSegment::unmap()
{
	ASSERT(_ptr);
	ERRCALL(munmap(_ptr,_size));
}

void FileMappingDynamicSegment::resize(size_t n)
{
	if(n==_truesize) return;

	if(n>_truesize)
	{
		if(n>_size)
		{
			unmap();
			_file.setSize(roundup(n));
			map();
		}
	}

	if(n<_size)
	{
		if(_size-n > 2*_growby)
		{
			unmap();
			_file.setSize(roundup(n));
			map();
		}

	}
	_truesize = n;
}


void FileMappingDynamicSegment::clear(size_t n)
{
	resize(n);
}

void FileMappingDynamicSegment::clear()
{
	unmap();
	_file.setSize(_growby);
	map();
	_truesize = 0;
}

size_t FileMappingDynamicSegment::roundup(size_t n)
{
	return n + _growby - (n % _growby);
}

size_t FileMappingDynamicSegment::size() const
{
	return _truesize;
}

const char * FileMappingDynamicSegment::cptr() const
{
	return _ptr;
}

char * FileMappingDynamicSegment::ptr() const
{
	return _ptr;
}


SGU_NS_END
